#include "json.h"
#include "cJSON/cJSON.h"

ProtocolCommand parse_command(cJSON *item) {
    ProtocolCommand ret;
    ret.name = cJSON_GetObjectItem(item, "name")->valuestring;
    ret.args = cJSON_GetObjectItem(item, "args")->valuestring;
    return ret;
}

ProtocolDevice parse_device(cJSON *item) {
    cJSON *screen = NULL;
    cJSON *command = NULL;
    ProtocolDevice ret;

    if (item == NULL) {
        return ret;
    }

    ret.deviceType = cJSON_GetObjectItem(item, "deviceType")->valuestring;
    ret.vendor = cJSON_GetObjectItem(item, "vendor")->valuestring;
    ret.jsHeapinK = cJSON_GetObjectItem(item, "JSHeapinK")->valueint;
    screen = cJSON_GetObjectItem(item, "Screen");
    if (screen != NULL) {
        ret.screen.width = cJSON_GetObjectItem(screen, "width")->valueint;
        ret.screen.height = cJSON_GetObjectItem(screen, "height")->valueint;
        ret.screen.shape = cJSON_GetObjectItem(screen, "shape")->valuestring;
    }
    command = cJSON_GetObjectItem(item, "commands");

    command = command->child;
    while (command != NULL) {
        ret.commands.push_back(parse_command(command));
        command = command->next;
    }

    return ret;
}

ProtocolMessage ProtocolMessage::parseMessage(std::string msg) {
    cJSON *root = NULL;
    cJSON *devices = NULL;
    cJSON *item = NULL;
    ProtocolMessage ret;

    root = cJSON_Parse(msg.data());
    if (!root) {
        return ret;
    }
    
    devices = cJSON_GetObjectItem(root, "devices");
    if (!devices) {
        return ret;
    }
    
    item = devices->child;
    while (item != NULL) {
        ret.devices.push_back(parse_device(item));
        item = item->next;
    }
    
    cJSON_Delete(root);
    return ret;
}

cJSON* stringify_command(ProtocolCommand cmd) {
    cJSON *ret = NULL;
    
    ret = cJSON_CreateObject();
    cJSON_AddStringToObject(ret, "name", cmd.name.data());
    cJSON_AddStringToObject(ret, "args", cmd.args.data());
    return ret;
}

cJSON* stringify_device(ProtocolDevice device) {
    cJSON *ret = NULL;
    cJSON *screen = NULL;
    cJSON *commands = NULL;
    int i;

    ret = cJSON_CreateObject();
    screen = cJSON_CreateObject();
    commands = cJSON_CreateArray();
    cJSON_AddStringToObject(ret, "deviceType", device.deviceType.data());
    cJSON_AddStringToObject(ret, "vendor", device.vendor.data());
    cJSON_AddNumberToObject(ret, "JSHeapinK", device.jsHeapinK);
    
    cJSON_AddNumberToObject(screen, "width", device.screen.width);
    cJSON_AddNumberToObject(screen, "height", device.screen.height);
    cJSON_AddStringToObject(screen, "shape", device.screen.shape.data());
    cJSON_AddItemToObject(ret, "Screen", screen);

    for (i = 0; i < device.commands.size(); i++) {
        cJSON_AddItemToArray(commands, stringify_command(device.commands[i]));
    }
    cJSON_AddItemToObject(ret, "commands", commands);
    return ret;
}

std::string ProtocolMessage::toString(){
    cJSON *root = NULL;
    cJSON *devices = NULL;
    int i;
    std::string ret = "";

    if (this == NULL) {
        return ret;
    }

    root = cJSON_CreateObject();
    devices = cJSON_CreateArray();

    for (i = 0; i < this->devices.size(); i++) {
        cJSON_AddItemToArray(devices, stringify_device(this->devices[i]));
    }
    cJSON_AddItemToObject(root, "devices", devices);
    ret = cJSON_Print(root);
    cJSON_Delete(root);
    return ret;
}